﻿//-----------------------------------------------------------------------------
// File: PropertyLoader.cs
// Author: Pierre Biermann (tauron031@gmail.com)
// Project: Bibliotek
// Namespace(s): MyLib.System.IO
//
// Desc: Klassen die Konfigurationswerte in Form einer Windows INI-Datei liest.
//
// (c) 2009-2011 by Pierre Biermann
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Reflection;
using System.IO;

namespace MyLib.System.IO
{
    /// <summary>
    /// Klassen die Konfigurationswerte in Form einer Windows INI-Datei liest.
    /// </summary>
    public class PropertyLoader
    {
        List<PropertySection> _sectionList = new List<PropertySection>();

        internal class PropertyEntry
        {
            public PropertyEntry(string source)
            {
                string[] splitStr = source.Split('=');
                PropertyName = splitStr[0].Trim();
                PropertyValue = splitStr[1].Trim();
            }

            public string PropertyName { get; set; }
            public string PropertyValue { get; set; }
        }

        internal class PropertySection
        {
            public string SectionName { get; set; }

            List<PropertyEntry> _propertys = new List<PropertyEntry>();
            public List<PropertyEntry> Propertys
            {
                get { return _propertys; }
            }
        }

        /// <summary>
        /// Erstellt eine PropertyLoader-Instanz.
        /// </summary>
        /// <param name="sourceData">Stringliste mit Einträgen</param>
        public PropertyLoader(string[] sourceData)
        {
            PropertySection section = null;
            foreach (string data in sourceData)
            {
                section = AddPropertyData(section, data);
            }
            if (section != null) 
                _sectionList.Add(section);
        }

        /// <summary>
        /// Erstellt eine PropertyLoader-Instanz.
        /// </summary>
        /// <param name="inputStream">Eingabedatenstrom</param>
        public PropertyLoader(Stream inputStream)
        {
            using (StreamReader reader = new StreamReader(inputStream))
            {
                PropertySection section = null;
                while (!reader.EndOfStream)
                {
                    string data = reader.ReadLine();
                    section = AddPropertyData(section, data);
                }
                if (section != null) 
                    _sectionList.Add(section);
            }
        }

        private PropertySection AddPropertyData(PropertySection section, string data)
        {
            if (!String.IsNullOrEmpty(data) && data[0] != ';')
            {
                Regex rex = new Regex("\\[(.*)\\]", RegexOptions.Compiled);
                Match m = rex.Match(data);
                if (m.Success)
                {
                    if (section != null) _sectionList.Add(section);
                    section = new PropertySection() { SectionName = m.Groups[1].Value };
                }
                else
                {
                    if (section != null && !String.IsNullOrEmpty(data))
                        section.Propertys.Add(new PropertyEntry(data));
                }
            }
            return section;
        }

        /// <summary>
        /// Prüft ob eine Eigenschaft in der angegebenen Sektion enthalten ist.
        /// </summary>
        /// <param name="propertyName">Name der Eigenschaft</param>
        /// <param name="sectionName">Name der Sektion</param>
        /// <returns>true, wenn die Eigenschaft vorhanden ist ansonsten false</returns>
        public bool HasProperty(string propertyName, string sectionName)
        {
            var section = (from sel in _sectionList where sel.SectionName == sectionName select sel).FirstOrDefault();
            if (section != null)
            {
                var entry = (from en in section.Propertys where en.PropertyName == propertyName select en).FirstOrDefault();
                return (entry != null);
            }
            return false;
        }

        /// <summary>
        /// Liefert eine Auflistung aller Sektionen.
        /// </summary>
        public IEnumerable<String> Sections
        {
            get
            {
                var sections = from s in _sectionList select s.SectionName;
                return sections;
            }
        }

        /// <summary>
        /// Schreibt alle Eigenschaften aus der angegebenen Sektion in das Ausgabeobjekt.
        /// </summary>
        /// <param name="outputObject">Ausgabeobjekt</param>
        /// <param name="sectionName">Sektionsname</param>
        public void GetPropertys(object outputObject, string sectionName)
        {
            if (outputObject == null)
                throw new NullReferenceException("outputObject");

            Type classType = outputObject.GetType();
            if (!classType.IsClass)
            {
                throw new
                    ArgumentException("Die in outputObject übergebene Referenz ist kein Klassentyp.");
            }

            var section = (from sel in _sectionList where sel.SectionName == sectionName select sel).FirstOrDefault();
            if (section != null)
            {
                foreach (PropertyEntry entry in section.Propertys)
                {
                    PropertyInfo pinfo = classType.GetProperty(entry.PropertyName);
                    if (pinfo != null)
                    {
                        object newType = Convert.ChangeType(entry.PropertyValue, pinfo.PropertyType);
                        pinfo.SetValue(outputObject, newType, null);
                    }
                }
            }
            else
            {
                throw new ArgumentException("Die angegebene Sektion konnte nicht gefunden werden.");
            }
        }
    }
}
